package org.simplesocket.simplenettymtbatis;

import lombok.extern.slf4j.Slf4j;
import org.simplesocket.simplenettycore.context.SimpleNettyContext;
import org.simplesocket.simplenettymtbatis.config.ConnectionPoolConfig;
import org.simplesocket.simplenettymtbatis.config.DatasourceConfig;
import org.simplesocket.simplenettymtbatis.datasource.Database;
import org.simplesocket.simplenettymtbatis.inject.InjectMapper;
import org.simplesocket.simplenettymtbatis.inject.MapperProxy;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;

@Slf4j
public class ContextHelper {
    
    public static void init(SimpleNettyContext simpleNettyContext,
                            SimpleNettyMybatisContext mybatisContext,
                            SimpleNettyMybatisConfig mybatisConfig){
        ConnectionPoolConfig connectionPoolConfig = mybatisConfig.getConnectionPoolConfig();
        if (connectionPoolConfig != null){
            DatasourceConfig.setConfigValue(connectionPoolConfig);
        }else {
            DatasourceConfig.setConfigValue(ConnectionPoolConfig.builder().build());
        }
        
        try {
            mybatisContext.setDatabase(Database.init(simpleNettyContext,mybatisConfig));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        loadClasses(simpleNettyContext,mybatisContext);
    }
    
    private static void loadClasses(SimpleNettyContext simpleNettyContext,
                                    SimpleNettyMybatisContext mybatisContext){
        loadInjectMappers(simpleNettyContext,mybatisContext);
    }
    
    private static void loadInjectMappers(SimpleNettyContext simpleNettyContext,
                                          SimpleNettyMybatisContext mybatisContext){
        log.info("Preparing to inject mappers ...");
        Map<String, Object> instances = simpleNettyContext.getInstances();
        for (String name : instances.keySet()) {
            Object instance = instances.get(name);
            Class<?> instanceClass = instance.getClass();
            Field[] fields = instanceClass.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(InjectMapper.class)){
                    continue;
                }
                Class<?> mapperType = field.getType();
                Object proxy;
                try {
                    proxy = Proxy.newProxyInstance(mapperType.getClassLoader(),
                            new Class<?>[]{mapperType}, new MapperProxy(mapperType,mybatisContext));
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
                
                try {
                    field.setAccessible(true);
                    field.set(instance,proxy);
                    log.info("Success to inject mapper '{}' to '{}'", name, field.getName());
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Can not auto-inject mapper in '"
                            + instanceClass.getTypeName() + "' in field '"
                            + field.getName() + "', because:\n\t" + e);
                }
            }
        }
        
        log.info("Injecting mappers successfully ...");
    }
    
}
